const classic_rules = {
	name : "classic 9x9x9",
	candidIDs : ['1','2','3','4','5','6','7','8','9'],
	cellIDs : [
		'c00','c01','c02','c03','c04','c05','c06','c07','c08',
		'c09','c10','c11','c12','c13','c14','c15','c16','c17',
		'c18','c19','c20','c21','c22','c23','c24','c25','c26',
		'c27','c28','c29','c30','c31','c32','c33','c34','c35',
		'c36','c37','c38','c39','c40','c41','c42','c43','c44',
		'c45','c46','c47','c48','c49','c50','c51','c52','c53',
		'c54','c55','c56','c57','c58','c59','c60','c61','c62',
		'c63','c64','c65','c66','c67','c68','c69','c70','c71',
		'c72','c73','c74','c75','c76','c77','c78','c79','c80'],
	houseIDs : [
		'hr0','hr1','hr2','hr3','hr4','hr5','hr6','hr7','hr8',
		'hc0','hc1','hc2','hc3','hc4','hc5','hc6','hc7','hc8',
		'hb0','hb1','hb2','hb3','hb4','hb5','hb6','hb7','hb8'],
	cellsToHouses: {
		"c00":["hr0","hc0","hb0"],"c01":["hr0","hc1","hb0"],"c02":["hr0","hc2","hb0"],
		"c03":["hr0","hc3","hb1"],"c04":["hr0","hc4","hb1"],"c05":["hr0","hc5","hb1"],
		"c06":["hr0","hc6","hb2"],"c07":["hr0","hc7","hb2"],"c08":["hr0","hc8","hb2"],
		"c09":["hr1","hc0","hb0"],"c10":["hr1","hc1","hb0"],"c11":["hr1","hc2","hb0"],
		"c12":["hr1","hc3","hb1"],"c13":["hr1","hc4","hb1"],"c14":["hr1","hc5","hb1"],
		"c15":["hr1","hc6","hb2"],"c16":["hr1","hc7","hb2"],"c17":["hr1","hc8","hb2"],
		"c18":["hr2","hc0","hb0"],"c19":["hr2","hc1","hb0"],"c20":["hr2","hc2","hb0"],
		"c21":["hr2","hc3","hb1"],"c22":["hr2","hc4","hb1"],"c23":["hr2","hc5","hb1"],
		"c24":["hr2","hc6","hb2"],"c25":["hr2","hc7","hb2"],"c26":["hr2","hc8","hb2"],
		"c27":["hr3","hc0","hb3"],"c28":["hr3","hc1","hb3"],"c29":["hr3","hc2","hb3"],
		"c30":["hr3","hc3","hb4"],"c31":["hr3","hc4","hb4"],"c32":["hr3","hc5","hb4"],
		"c33":["hr3","hc6","hb5"],"c34":["hr3","hc7","hb5"],"c35":["hr3","hc8","hb5"],
		"c36":["hr4","hc0","hb3"],"c37":["hr4","hc1","hb3"],"c38":["hr4","hc2","hb3"],
		"c39":["hr4","hc3","hb4"],"c40":["hr4","hc4","hb4"],"c41":["hr4","hc5","hb4"],
		"c42":["hr4","hc6","hb5"],"c43":["hr4","hc7","hb5"],"c44":["hr4","hc8","hb5"],
		"c45":["hr5","hc0","hb3"],"c46":["hr5","hc1","hb3"],"c47":["hr5","hc2","hb3"],
		"c48":["hr5","hc3","hb4"],"c49":["hr5","hc4","hb4"],"c50":["hr5","hc5","hb4"],
		"c51":["hr5","hc6","hb5"],"c52":["hr5","hc7","hb5"],"c53":["hr5","hc8","hb5"],
		"c54":["hr6","hc0","hb6"],"c55":["hr6","hc1","hb6"],"c56":["hr6","hc2","hb6"],
		"c57":["hr6","hc3","hb7"],"c58":["hr6","hc4","hb7"],"c59":["hr6","hc5","hb7"],
		"c60":["hr6","hc6","hb8"],"c61":["hr6","hc7","hb8"],"c62":["hr6","hc8","hb8"],
		"c63":["hr7","hc0","hb6"],"c64":["hr7","hc1","hb6"],"c65":["hr7","hc2","hb6"],
		"c66":["hr7","hc3","hb7"],"c67":["hr7","hc4","hb7"],"c68":["hr7","hc5","hb7"],
		"c69":["hr7","hc6","hb8"],"c70":["hr7","hc7","hb8"],"c71":["hr7","hc8","hb8"],
		"c72":["hr8","hc0","hb6"],"c73":["hr8","hc1","hb6"],"c74":["hr8","hc2","hb6"],
		"c75":["hr8","hc3","hb7"],"c76":["hr8","hc4","hb7"],"c77":["hr8","hc5","hb7"],
		"c78":["hr8","hc6","hb8"],"c79":["hr8","hc7","hb8"],"c80":["hr8","hc8","hb8"]},
	houses: {
		'hr0': ['c00','c01','c02','c03','c04','c05','c06','c07','c08'],
		'hr1': ['c09','c10','c11','c12','c13','c14','c15','c16','c17'],
		'hr2': ['c18','c19','c20','c21','c22','c23','c24','c25','c26'],
		'hr3': ['c27','c28','c29','c30','c31','c32','c33','c34','c35'],
		'hr4': ['c36','c37','c38','c39','c40','c41','c42','c43','c44'],
		'hr5': ['c45','c46','c47','c48','c49','c50','c51','c52','c53'],
		'hr6': ['c54','c55','c56','c57','c58','c59','c60','c61','c62'],
		'hr7': ['c63','c64','c65','c66','c67','c68','c69','c70','c71'],
		'hr8': ['c72','c73','c74','c75','c76','c77','c78','c79','c80'],
		'hc0': ['c00','c09','c18','c27','c36','c45','c54','c63','c72'],
		'hc1': ['c01','c10','c19','c28','c37','c46','c55','c64','c73'],
		'hc2': ['c02','c11','c20','c29','c38','c47','c56','c65','c74'],
		'hc3': ['c03','c12','c21','c30','c39','c48','c57','c66','c75'],
		'hc4': ['c04','c13','c22','c31','c40','c49','c58','c67','c76'],
		'hc5': ['c05','c14','c23','c32','c41','c50','c59','c68','c77'],
		'hc6': ['c06','c15','c24','c33','c42','c51','c60','c69','c78'],
		'hc7': ['c07','c16','c25','c34','c43','c52','c61','c70','c79'],
		'hc8': ['c08','c17','c26','c35','c44','c53','c62','c71','c80'],
		'hb0': ['c00','c01','c02','c09','c10','c11','c18','c20','c19'],
		'hb1': ['c03','c04','c05','c12','c13','c14','c21','c22','c23'],
		'hb2': ['c06','c07','c08','c15','c16','c17','c24','c25','c26'],
		'hb3': ['c27','c28','c29','c36','c37','c38','c45','c46','c47'],
		'hb4': ['c30','c31','c32','c39','c40','c41','c48','c49','c50'],
		'hb5': ['c33','c34','c35','c42','c43','c44','c51','c52','c53'],
		'hb6': ['c54','c55','c56','c63','c64','c65','c72','c73','c74'],
		'hb7': ['c57','c58','c59','c66','c67','c68','c75','c76','c77'],
		'hb8': ['c60','c61','c62','c69','c70','c71','c78','c79','c80']
	},
	cellOffsets :{
		'c00':{y:0,x:0,},'c01':{y:0,x:11.11,},'c02':{y:0,x:22.22,},
		'c03':{y:0,x:33.33,},'c04':{y:0,x:44.44,},'c05':{y:0,x:55.55,},
		'c06':{y:0,x:66.66,},'c07':{y:0,x:77.77,},'c08':{y:0,x:88.88,},
		'c09':{y:11.11,x:0,},'c10':{y:11.11,x:11.11,},'c11':{y:11.11,x:22.22,},
		'c12':{y:11.11,x:33.33,},'c13':{y:11.11,x:44.44,},'c14':{y:11.11,x:55.55,},
		'c15':{y:11.11,x:66.66,},'c16':{y:11.11,x:77.77,},'c17':{y:11.11,x:88.88,},
		'c18':{y:22.22,x:0,},'c19':{y:22.22,x:11.11,},'c20':{y:22.22,x:22.22,},
		'c21':{y:22.22,x:33.33,},'c22':{y:22.22,x:44.44,},'c23':{y:22.22,x:55.55,},
		'c24':{y:22.22,x:66.66,},'c25':{y:22.22,x:77.77,},'c26':{y:22.22,x:88.88,},
		'c27':{y:33.33,x:0,},'c28':{y:33.33,x:11.11,},'c29':{y:33.33,x:22.22,},
		'c30':{y:33.33,x:33.33,},'c31':{y:33.33,x:44.44,},'c32':{y:33.33,x:55.55,},
		'c33':{y:33.33,x:66.66,},'c34':{y:33.33,x:77.77,},'c35':{y:33.33,x:88.88,},
		'c36':{y:44.44,x:0,},'c37':{y:44.44,x:11.11,},'c38':{y:44.44,x:22.22,},
		'c39':{y:44.44,x:33.33,},'c40':{y:44.44,x:44.44,},'c41':{y:44.44,x:55.55,},
		'c42':{y:44.44,x:66.66,},'c43':{y:44.44,x:77.77,},'c44':{y:44.44,x:88.88,},
		'c45':{y:55.55,x:0,},'c46':{y:55.55,x:11.11,},'c47':{y:55.55,x:22.22,},
		'c48':{y:55.55,x:33.33,},'c49':{y:55.55,x:44.44,},'c50':{y:55.55,x:55.55,},
		'c51':{y:55.55,x:66.66,},'c52':{y:55.55,x:77.77,},'c53':{y:55.55,x:88.88,},
		'c54':{y:66.66,x:0,},'c55':{y:66.66,x:11.11,},'c56':{y:66.66,x:22.22,},
		'c57':{y:66.66,x:33.33,},'c58':{y:66.66,x:44.44,},'c59':{y:66.66,x:55.55,},
		'c60':{y:66.66,x:66.66,},'c61':{y:66.66,x:77.77,},'c62':{y:66.66,x:88.88,},
		'c63':{y:77.77,x:0,},'c64':{y:77.77,x:11.11,},'c65':{y:77.77,x:22.22,},
		'c66':{y:77.77,x:33.33,},'c67':{y:77.77,x:44.44,},'c68':{y:77.77,x:55.55,},
		'c69':{y:77.77,x:66.66,},'c70':{y:77.77,x:77.77,},'c71':{y:77.77,x:88.88,},
		'c72':{y:88.88,x:0,},'c73':{y:88.88,x:11.11,},'c74':{y:88.88,x:22.22,},
		'c75':{y:88.88,x:33.33,},'c76':{y:88.88,x:44.44,},'c77':{y:88.88,x:55.55,},
		'c78':{y:88.88,x:66.66,},'c79':{y:88.88,x:77.77,},'c80':{y:88.88,x:88.88,}},
	candidOffsets:{
		1:{x:12,y:6},
		2:{x:42,y:6},
		3:{x:72,y:6},
		4:{x:12,y:36},
		5:{x:42,y:36},
		6:{x:72,y:36},
		7:{x:12,y:66},
		8:{x:42,y:66},
		9:{x:72,y:66},
	},
}

function parse( sz){
	var field = new Array(),
		id,
		candid,
		houses=new Array();
	for( var i=0; i<sz.length; i++) {
		houses = new Array()
		id= classic_rules.cellIDs[i];
		candid = sz[i]=="0"? classic_rules.candidIDs :sz[i];
		for( var [hid,cids] in Iterator( classic_rules.houses))
			if( !!~cids.indexOf( id))
				houses.push(hid);
		field.push( new sdkCell(id, candid, houses));
	}
	return field;
}


function sdkCell(){
	this.id = null;
	this.candids = null;
	this.houses = null;
	switch( arguments.length){
		case 1:
			if( typeof arguments[0] == "string" || arguments[0] instanceof String)
				this.id = arguments[0];
			else {
				this.id = arguments[0].id || null;
				this.candids = arguments[0].candids || null;
				this.houses = arguments[0].houses || null;
			}
			break;
		case 2:
			this.id = arguments[0];
			if( typeof arguments[1] == "array" || arguments[1] instanceof Array)
				this.candids = arguments[1];
			else
				this.candids = new Array( arguments[1]);
			break;
		case 3:
			this.id = arguments[0];
			if( typeof arguments[1] == "array" || arguments[1] instanceof Array)
				this.candids = arguments[1];
			else
				this.candids = new Array( arguments[1]);
			this.houses = arguments[2];
			break;
	}
	
	this.contains = function ( candid) {
		return !!~this.candids.indexOf(candid);
	};
	
	this.isSolved = function (){ 
		return this.candids.length == 1;
	};
	
	this.enable = function( candid){
		if( !this.contains( candid))
			this.candids.push( candid);
	};
	
	this.disable = function( candid){
		if( candid instanceof Array)
			candid.forEach(function(CandidId){
				this.disable(CandidId);
			},this);
		else
			this.candids = this.candids.filter(function(Candid){
				return Candid != candid;
			});
	};
	
	this.only = function( candid){
		if( this.candids.length == 1){
			this.candids=classic_rules.candidIDs;
			return this;
		}
		this.candids = new Array(1);
		this.candids[0] = candid;
		
		return this;
	};
	
	this.swap = function ( candid){	
		if( this.contains( candid))
			this.disable( candid);
		else
			this.enable( candid);
		return this;
	};
	
	this.isInHouse = function( houseId){
		return !!~this.houses.indexOf( houseId );
	};
	
	this.applyHouses = function( new_houses){
		this.houses = new Array();
		for( var i =0; i< new_houses.length; i++){
			if( new_houses[ i].indexOf( this.id) >= 0)
				this.houses.push(i);
		}
	};
}

function sdkChange( cmd, cellIDs, candidIDs){
	return {
		cmd: cmd,
		cellIDs: cellIDs,
		candidIDs: candidIDs,
		apply: function( slice){
			var a = sdkSlice( slice.filterBy(function(cell){
				return !!~this.cellIDs.indexOf( cell.id);
			},this));
			
			var b = slice.filterBy(function(cell){
				return !~this.cellIDs.indexOf( cell.id);
			},this);
			
			if( cmd === "swap")
				a=a.swap( this.candidIDs[0]);
				
			if( cmd === "only")
				a=a.only( this.candidIDs[0]);
			b=b.concat(a);
			return sdkSlice(b);
		}
	};
}

function sdkSlice( data){
	sdkHelper.makeSlice__.call( data);
	return data;
}

function sliceBase(){
		
	this.isSlice = function(){
		return true;
	};
	
	/*this.calcHouses = function(){
		var h2 = new Array();
		this.forEach(function(cell){
			h2=h2.concat( cell.houses);
		},this);
		
		this.houses = sdkHelper.makeUnique( h2 ); 
	};*/
	
	this.houseIterator = function(){
		return sdkHelper.makeUnique( this.houses);
	};
	
	this.isSolved = function(){
		return this.every(function( cell){
			cell.isSolved();
		});
	};
	
	this.disable = function( candid){
		return this.forEach( function(cell){
			cell.disable(candid);
		});
	};
	
	this.swap = function( candidID){
		return sdkSlice(this.map( function(cell){
			return cell.swap( candidID);
		}));
	};
	
	this.only = function( candidID){
		return sdkSlice(this.map( function(cell){
			return cell.only( candidID);
		}));
	};
	
	// Filter 
	this.filterBy = function( callback, context){
		var temp = [];
		temp = this.filter( callback, context);
		return sdkSlice( temp);
	};
	
	this.singleCell = function( arg){
		return this.filterBy( function( cell){
				return cell.id == arg;
			});
	};
	
	this.secludeCell = function( arg){
		return this.filterBy(function(cell){
				return cell.id != arg;
			});
	};
	
	this.filterByHouse = function( arg){
		return this.filterBy(function (cell) {
				return cell.isInHouse( arg);
			},arg);
	};
	
	this.filterByHouses = function( arg){
		var ret = new Array();		
		var i=0;
		arg.forEach(function( houseId){
			ret = ret.concat( this.filterByHouse( houseId));
		},this);
		ret = sdkHelper.makeUniqueCells( ret);
		sdkHelper.makeSlice( ret );
		return ret;
	};
	
	this.filterByLength = function( arg){
		return this.filterBy( function (cell) {
				return cell.candids.length == arg;
			},arg);
	};
	
	this.filterByCandid = function( arg){
		return this.filterBy( function (cell) {
				return cell.contains( arg);
			},arg);
	};
	
	this.filterByCandids = function( arg){
		var ret = this;
		//ret = ret.concat( this);

		arg.forEach(function(candidId){
			ret = ret.filterByCandid(candidId);
		},this);

		return ret;
	};
	
	// Negations
	this.filterNotHouse = function( arg){
		return this.filterBy(function (cell) {
				return !cell.isInHouse( arg);
			},arg);
	};
	
	this.filterNotHouses = function( arg){
		var ret = copy(this);
		arg.forEach(function( houseId){
			ret.concat( ret.filterNotHouse( houseId));
		});
		return ret;
	};
	
	this.filterNotLength = function( arg){
		return this.filterBy( function (cell) {
				return cell.candids.length != arg;
			},arg);
	};
	
	this.filterNotCandid = function( arg){
		return this.filterBy( function (cell) {
				return !cell.contains( arg);
			},arg);
	};
	
	this.filterNotCandids = function( arg){
		var ret = this;
		arg.forEach(function(candidId){
			ret.concat( ret.filterNotCandid(candidId));
		});
		return ret;
	};
	
	//this.calcHouses();
}

var sdkHelper = {
	makeSlice : function ( who) {
		this.makeSlice__.call( who);
		return who;
	},
	makeSlice__ : sliceBase,
	
	makeUnique : function( arr){
		arr = arr.sort();
		var arr2 = new Array();
		for( i =0; i< arr.length; i++){
			arr2.push( arr[i]);
			i = arr.lastIndexOf(arr[i]);
		}
		return arr2;
	},
	
	makeUniqueCells : function( arr){
		arr = arr.sort();
		
		var arr2 = new Array();
		for( i =0; i< arr.length; i++){
		
			arr2.push( arr[i]);

			var j=0;
			for( j= i+1; j< arr.length ; j++){
				
				if( arr[ j].id != arr[ i].id){
					break;
				}
			}

			i=j-1;

			
		}
		sdkHelper.makeSlice( arr2)
		return arr2;
	},
	
	setUnion : function(a1,a2){
		return sdkHelper.makeUnique(Array.concat(a1,a2));
	},
	
	setIntersection : function( a1, a2){
		var ret = new Array();
		a1.forEach(function(ele){
			if( !!~a2.indexOf(ele))
				ret.push(ele);
		});
		return sdkHelper.makeUnique(ret);
	},
	
	setSymDiff : function( a1, a2){
		var ret = new Array();
		a1.forEach(function(ele){
			if( !~a2.indexOf(ele))
				ret.push(ele);
		});
		a2.forEach(function(ele){
			if( !~a1.indexOf(ele))
				ret.push(ele);
		});
		return sdkHelper.makeUnique(ret);
	},
	
	sharedHouses : function( c1, c2 ){
		var ret = new Array();
		ret = sdkHelper.setIntersection( c1.houses, c2.houses);
		return ret;
	},
	
	sharedCandids : function( c1, c2){
		var ret = new Array();
		for( candidId in c1.candids)
			if( c2.contains( c2))
				ret.push( candidId);
		return ret;
	},
};

function subsetCounting( data, set){
	var count = 0;

}

var algors = {
	"Naked Single":function ( data){
			var ret = 0;
			data.filterByLength(1).forEach( function(solvedCell){
				solvedCell.houses.forEach( function(sCHouseId){
					data.filterByHouse( sCHouseId).filterNotLength(1).disable( solvedCell.candids[0]);
				},this);
			},this);
			
			return data;
		},
	"Hidden Single" : function( data){
			var ret=0;
			var grml = data;
			sdkHelper.makeSlice(grml);
			for each( var house in grml.houseIterator()){
				for each( candidId in [1,2,3,4,5,6,7,8,9]){
					var blub = grml.filterBy(function(cell){
						return cell.isInHouse( house) && cell.contains(candidId);
					});
					//var blub = grml.filterByHouse( house).filterByCandid( candidId);
					if( blub.length == 1)
						if( blub[0].candids.length != 1){
							blub[0].only( candidId);
							ret++;
						}
				}
			}
			
			return grml;
		}
	};





var field = [];
var history = [];

var handler = [];

function regHandler( cmd, callback, context){
	handler.push( {cmd:cmd,callback:callback,context:context});
}



var intercom = {
	handler: [],
	register: function(cmd, callback, context){
		this.handler.push( {"cmd":cmd,"callback":callback,"context":context});
	},
	catcher : function( e){
		var ret = {},
			fn = this.handler.filter( function( handle){
				return handle.cmd === e.data.cmd;
			},this);
		
		if( fn.length ==0)
			return "fail.";
		if( fn.length >1)
			return "too many handlers";
		fn=fn[0];
		try{
			ret = fn.callback.call(fn.context,e.data);
		}catch(error){
			note(e.data.cmd);
			
			throw(error);
		}
		if(!ret.refid)
			ret.refid = e.data.refid;
		postMessage( ret);
	}
};

function cl( a){
	postMessage({cmd:"note",args:{value:a}});
}

function note( a){
	postMessage({cmd:"note",args:{value:a}});
}

function ns( a){
	postMessage({cmd:"log",args:{value:a.toSource()}});
}

function ne( a){
	postMessage({cmd:"error",args:{value:a}});
}

function es( a){
	postMessage({cmd:"logerror",args:{value:a}});
}

function tempCatcher(e){
	intercom.catcher(e);
}

onmessage = tempCatcher;

intercom.register("init", function(e){
	field = sdkSlice( parse( e.args.field));
	return getHandler({args:{value:"field"}});
});

function getHandler(e){
	switch( e.args.value){
		case "field":
			return {
				cmd: "post_field",
				args: {
					value: field
				}
			};
		case "cell":
			return {
				cmd: "post_cell",
				args: {
					value: field.singleCell(e.args.cellID)
				}
			};
		case "cellIDs":
			return {
				cmd: "post_cellIDs",
				args:{
					value: classic_rules.cellIDs
				}
			};
		case "candidIDs":
			return {
				cmd: "post_candidIDs",
				args:{
					value: classic_rules.candidIDs
				}
			};
		case "cellsToHouses":
			return {
				cmd: "post_cellToHouses",
				args:{
					value: classic_rules.cellsToHouses
				}
			};
		case "all_candids":
			return {
				cmd: "post_all_candids",
				args:{
					value: classic_rules.candidIDs
				}
			};
			break;
		default:
			if(classic_rules[e.args.value]){
				return {
					cmd: "post_"+e.args.value,
					args:{
						value: classic_rules[e.args.value]
					}
				};
			}
			return undefined;
	}
}
intercom.register("get", getHandler);

intercom.register("get_multiple",function(e){
	var ret = {};
	for( var i=0; i<e.args.values.length;i++){
		var req= e.args.values[i];
		ret[req]=getHandler({args:{value:req}}).args.value;
	}
	return {cmd:"post_multiple",args:ret};
});
intercom.register("status",function(e){
	return {
		cmd:"log",
		args:{
			value:"all clear! :D"
		}
	};
});
intercom.register("change",function(e){
switch( e.args.value){
	case "swap":
		history.unshift(new sdkChange("swap",[e.args.cellID],[e.args.candidID]));
		field = history[0].apply( field);
		return getHandler({args:{value:"cell",cellID:e.args.cellID}});
		break;
	case "only":
		history.unshift(new sdkChange("only",[e.args.cellID],[e.args.candidID]));
		field = history[0].apply( field);
		return getHandler({args:{value:"cell",cellID:e.args.cellID}});
}});
intercom.register("calc",function(e){
	if( algors[e.args.value]){
		field = algors[e.args.value](field);
		return getHandler({args:{value:"field"}});
	}
});


